import asyncore, socket, re, time, sys

pat200OK       = re.compile(r"200 OK\r\n")
patXACKMessage = re.compile(r"X-ACKMessage: ([0-9]+)\r\n")
patMessageID   = re.compile(r"X-MessageID: ([0-9]+)\r\n")


def returnMatchesAndTruncatedString(pat, input, prefix='', suffix=''):
    m = pat.findall(input)
    if m:
        lastMatch = prefix + m[-1] + suffix
        lastPos = input.rfind(lastMatch)
        return (m, input[lastPos + len(lastMatch):])
    return ([], input)
    

# The producer produces messages as soon as it receives a 200 OK from the server.
class message_producer(asyncore.dispatcher):

    def __init__(self, host, port, queue, data, limit=1):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect( (host, port) )
        (self.host, self.port, self.queue, self.data, self.limit) = (host, port, queue, data, limit)
        self.messageCount = 0
        self.inBuffer = ''
        self.howManyCanISend = 1
        self.once = True
        
    def HTTPRequest(self, data):
        return 'GET /queue/%s/produceMessage/ HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\nContent-Length: %d\r\n\r\n%s' % \
            (self.queue, self.host, len(data), data)
        
    def handle_expt(self):
        print "Producer: There was an error"
        self.handle_close()
        
    def handle_connect(self):
        pass

    def handle_close(self):
        self.close()

    def handle_read(self):
        try:
            self.inBuffer += self.recv(8192)
            (m, self.inBuffer) = returnMatchesAndTruncatedString(pat200OK, self.inBuffer)
            self.howManyCanISend += len(m)
            self.messageCount += len(m)
            if self.messageCount >= self.limit:
                self.handle_close()
            # print "producer: in buffer: " + str(self.inBuffer)
        except Exception, e:
            print "Exception: message_producer: " + str(e)

    def writable(self):
        return self.howManyCanISend > 0

        if self.once:
            self.once = False
            return True
        return self.once


    def handle_write(self):
        self.howManyCanISend -= 1
        self.send(self.HTTPRequest(self.data))


class message_consumer(asyncore.dispatcher):

    def __init__(self, host, port, queue, limit=1):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect( (host, port) )
        (self.host, self.port, self.queue, self.limit) = (host, port, queue, limit)
        self.messageCount = 0
        self.messagesToACK = [0]
        self.once = True
        self.inBuffer = ""

    def HTTPRequest(self, ackMessage=None):
        ackString = ""
        if ackMessage:
            ackString = "X-ACKMessage: %d\r\n" % ackMessage
        req = 'GET /queue/%s/consumeMessage/ HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n%s\r\n' % \
            (self.queue, self.host, ackString)
        return req
    
    def handle_expt(self):
        print "Consumer: There was an error"
        self.handle_close()
        
    def handle_connect(self):
        pass

    def handle_close(self):
        self.close()

    def handle_read(self):
        try:
            self.inBuffer += self.recv(8192)
            (m, self.inBuffer) = returnMatchesAndTruncatedString(patMessageID, self.inBuffer, prefix="X-MessageID: ")
            m.reverse()
            # Prepend to list of ACKs
            self.messagesToACK[:0] = map(lambda mid: int(mid), m)
            # print "consumer: in buffer: " + str(self.inBuffer)
            self.messageCount += len(m)
            if self.messageCount >= self.limit:
                self.handle_close()

        except Exception, e:
            print "Exception: message_consumer: " + str(e)
        

    def writable(self):
        # Write only if we have an ACK to send
        if self.messagesToACK:
            return True
        else:
            return False

        if self.once:
            self.once = False
            return True            
        return self.once
        

    def handle_write(self):
        messageToAck = self.messagesToACK[-1]
        self.messagesToACK = self.messagesToACK[:-1]
        self.send(self.HTTPRequest(messageToAck))


def createRequestResponsePair(queueName):
    # "Sending a small Message"
    s = "XYZABCPQRL".join(["" for i in xrange(0,100) ])
    producer = message_producer("localhost", 8070, queueName, s, 5000)
    consumer = message_consumer("localhost", 8070, queueName, 4999)

createRequestResponsePair(sys.argv[1])
# createRequestResponsePair(sys.argv[1])
# createRequestResponsePair(sys.argv[1])
# createRequestResponsePair(sys.argv[1])
# createRequestResponsePair(sys.argv[1])
# createRequestResponsePair(sys.argv[1])

start = time.time()
asyncore.loop()
end = time.time()

print "Time taken: " + str(end - start) + " second"